package qj.tool.jna;

import java.awt.Point;
import java.util.*;
import java.util.Map.Entry;

import qj.util.ReflectUtil;
import qj.util.SystemUtil;
import qj.util.funct.*;

public class MouseHandler {
    
	private static final HashSet<Integer> keysPressed = new HashSet<Integer>();
	
	private static Map<F3<Integer, Boolean, Point, Boolean>, F3<Integer, Boolean, Point, Boolean>> 
	keyListeners = new HashMap<F3<Integer, Boolean, Point, Boolean>, F3<Integer, Boolean, Point, Boolean>>();
	
	private static ArrayList<Runnable> moveListeners = new ArrayList<Runnable>();

	private static F1<Point, Boolean> onMove = new F1<Point, Boolean>() {public Boolean e(Point point) {
		for (Runnable runnable : moveListeners) {
			runnable.run();
		}
		return moveEnabled;
	}};
	public static F3<Integer, Boolean, Point, Boolean> onButton = new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean pressed, Point point) {

		if (pressed && !keysPressed.contains(key)) {
			keysPressed.add(key);
		} else if (!pressed) {
			keysPressed.remove(key);
		}
		
		// Inform
		for (Entry<F3<Integer, Boolean, Point, Boolean>, F3<Integer, Boolean, Point, Boolean>> entry : keyListeners.entrySet()) {
		    if (entry.getKey().e(key, pressed, point)) {
				F3<Integer, Boolean, Point, Boolean> listener = entry.getValue();
				if (listener != null) {
					Boolean result = listener.e(key, pressed, point);
					if (result == null || !result) {
						return false;
					}
				}
			}
		}
		return true;
	}};

	private static boolean moveEnabled = true;
	
	static {
		if (SystemUtil.isWindows()) {
			JnaUtil.setMouseHook(
					onMove,
					onButton);
    	} else {
    		ReflectUtil.invoke(ReflectUtil.getMethod("init", ReflectUtil.forName("qj.tool.jna.MouseHooks")), null);
		}
    }

	public static void listenAllButton(Runnable runnable) {
		listenAllButton(Fs.<Integer, Boolean, Point>p3(runnable));
	}
//	public static void listenButton(int button,Runnable runnable) {
//		keyListeners.put(
//				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer a, Boolean b, Point c) {
//					return true;
//				}},
//				Fs.f3(runnable, true)
//		);
//	}

	public static void listenAllButton(final P3<Integer, Boolean, Point> listener) {
		keyListeners.put(
				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer a, Boolean b, Point c) {
					return true;
				}},
				Fs.f3(listener, true)
		);
	}
	
	public static void listenAll(Runnable runnable) {
		listenAllButton(runnable);
		listenMove(runnable);
	}

	private static void listenMove(Runnable runnable) {
		moveListeners.add(runnable);
	}

//	public static void map(final F2<Integer, Boolean, Boolean> condition, final int toKey) {
//		keyListeners.put(
//			Fs.addParam(condition, Point.class), 
//			new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean pressed, Point point) {
//				if (pressed) {
//					DesktopUI4.mousePress(toKey);
//				} else {
//					DesktopUI4.mouseRelease(toKey);
//				}
//				return false;
//			}}
//		);
//	}

//	public static void map(final int btn, F2<Integer, Boolean, Boolean> then) {
//		keyListeners.put(
//				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean b, Point c) {
//					return btn == key;
//				}}, 
//				Fs.addParam(then, Point.class)
//			);
//	}

//	public static void map(final int btn, P2<Integer, Boolean> then) {
//		keyListeners.put(
//				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean b, Point c) {
//					return btn == key;
//				}}, 
//				Fs.toF3(then, Point.class, false)
//			);
//	}
	public static void map(final int btn, final F0<Boolean> then) {
		keyListeners.put(
				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean press, Point c) {
					return btn == key && press;
				}}, 
				new F3<Integer, Boolean, Point, Boolean>() {
					
					public Boolean e(Integer a, Boolean b, Point c) {
						return then.e();
					}
				}
			);
	}
	public static void mapRelease(final int btn, final F0<Boolean> then) {
		keyListeners.put(
				new F3<Integer, Boolean, Point, Boolean>() {public Boolean e(Integer key, Boolean press, Point c) {
					return btn == key && !press;
				}}, 
				new F3<Integer, Boolean, Point, Boolean>() {
					
					public Boolean e(Integer a, Boolean b, Point c) {
						return then.e();
					}
				}
			);
	}
//	public static void map(final F2<Integer, Boolean, Boolean> condition, final P2<Integer, Boolean> then) {
//		keyListeners.put(
//			Fs.addParam(condition, Point.class), 
//			Fs.toF3(then, Point.class, false)
//		);
//	}

//	public static void map(int btn, final String sequence) {
//		map(btn, 
//			new P2<Integer, Boolean>() {public void e(Integer btn, Boolean pressed) {
//				if (pressed) {
//					DesktopUI4.type(sequence);
//				}
//			}
//		});
//	}
	
	public static void enableMove(boolean enableMove) {
		moveEnabled = enableMove;
	}

	public static boolean isPressed(int btn) {
		return keysPressed.contains(btn);
	}

}
